package com.xuecheng.manage_media.service;

import com.alibaba.fastjson.JSONObject;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.response.CheckChunkResult;
import com.xuecheng.framework.domain.media.response.MediaCode;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.DataResponseResult;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.manage_media.config.RabbitMQConfig;
import com.xuecheng.manage_media.dao.MediaFileRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MediaUploadService {
    @Value("${xc-service-manage-media.upload-location}")
    private String uploadPath;

    @Resource
    private MediaFileRepository mediaFileRepository;
    @Resource
    private RabbitTemplate rabbitTemplate;

    private String getFilePath(String fileMd5,String fileExt){
        String filePath = uploadPath + fileMd5.substring(0,1) + File.separator + fileMd5.substring(1,2) + File.separator + fileMd5 + File.separator + fileMd5 + "." + fileExt;
        return filePath;
    }

    private String getFileFolderRelativePath(String fileMd5){
        //String fileFolderRelativePath =  fileMd5.substring(0,1) + File.separator + fileMd5.substring(1,2) + File.separator + fileMd5 + File.separator;
        String fileFolderRelativePath =  fileMd5.substring(0,1) + "/" + fileMd5.substring(1,2) + "/" + fileMd5 + "/";
        return fileFolderRelativePath;
    }

    private String getFileFolderPath(String fileMd5){
        String fileFolderPath = uploadPath + getFileFolderRelativePath(fileMd5);
        return fileFolderPath;
    }

    private boolean createChunkFolder(String fileMd5){
        String folderPath = getChunkFolder(fileMd5);
        File folder = new File(folderPath);

        if(!folder.exists()){
            boolean OK = folder.mkdirs();
            return OK;
        }else{
            //不能清空内容，这些是已经上传过的就不需要再次上传相当于断点续传了。
            //boolean clearOK = clear(folder);
            //return clearOK;
            return true;
        }
    }

    private boolean clear(File folder){
        if(Objects.isNull(folder)){
            return false;
        }
        File[] files = folder.listFiles();
        if(Objects.nonNull(files)){
            for (File file : files) {
                if(file.isFile()){
                    boolean deleteOK = file.delete();
                    if(!deleteOK){
                        return false;
                    }
                }else{
                    boolean clearOK = clear(file);
                    if(!clearOK){
                        return false;
                    }
                }
            }
        }
        return true;
    }

    private boolean createFileFolder(String fileMd5){
        String fileFolderPath = getFileFolderPath(fileMd5);
        File fileFolder = new File(fileFolderPath);

        if(!fileFolder.exists()){
            boolean OK = fileFolder.mkdirs();
            return OK;
        }
        return true;
    }

    public ResponseResult register(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt){
        /*
        获取文件
        判断文件是否存在
         */
        String filePath = getFilePath(fileMd5, fileExt);
        File file = new File(filePath);

        Optional<MediaFile> mediaFileOptional = mediaFileRepository.findById(fileMd5);
        //存在
        if(file.exists() && mediaFileOptional.isPresent()){
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_EXIST);
        }

        if(file.exists() && !mediaFileOptional.isPresent()){
            //保存到mongoDB
            MediaFile mediaFile = new MediaFile();
            mediaFile.setFileId(fileMd5);
            mediaFile.setFileName(fileMd5 + "." + fileExt);
            mediaFile.setFileOriginalName(fileName);
            mediaFile.setFileSize(fileSize);
            mediaFile.setFilePath(getFileFolderRelativePath(fileMd5));
            mediaFile.setMimeType(mimetype);
            mediaFile.setFileType(fileExt);
            mediaFile.setFileStatus("301002");
            mediaFile.setUploadTime(new Date());
            MediaFile save = mediaFileRepository.save(mediaFile);
            return new ResponseResult(MediaCode.UPLOAD_FILE_REGISTER_FILE_UPLOAD_SUCCESS);
        }

        //不存在
        boolean createOK = createFileFolder(fileMd5);
        boolean chunkFolderOK = createChunkFolder(fileMd5);
        if(!createOK || !chunkFolderOK){
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_CREATEFOLDER_FAIL);
        }

        //获取chunkArray数组
        List<Map<String, Object>> chunkArray = getChunkArray(fileMd5);
        return DataResponseResult.success(chunkArray);
    }

    private List<Map<String, Object>> getChunkArray(String fileMd5) {
        String chunkFolder = getChunkFolder(fileMd5);
        File folder = new File(chunkFolder);
        List<File> chunkFileList = getChunkFileList(folder);
        List<Map<String, Object>> chunkList = chunkFileList.stream().map(file -> {
            Map<String, Object> chunk = new HashMap<>();
            chunk.put("index", Integer.parseInt(file.getName()));
            chunk.put("size", file.length());
            return chunk;
        }).collect(Collectors.toList());
        return chunkList;
    }

    private String getChunkFolder(String fileMd5){
        return getFileFolderPath(fileMd5) + "chunks" + File.separator;
    }

    public CheckChunkResult checkChunk(String fileMd5, String chunk, String chunkSize){
        String chunkFolderPath = getChunkFolder(fileMd5);
        File chunkFile = new File(chunkFolderPath + chunk);
        if(chunkFile.exists()){
            return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK, true);
        }else{
            return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK, false);
        }
    }

    public ResponseResult uploadChunk(MultipartFile file, String fileMd5, String chunk){
        // 获取文件对象，对写
        String chunkFolderPath = getChunkFolder(fileMd5);
        File chunkFile = new File(chunkFolderPath + chunk);
        if(!chunkFile.exists()){
            try {
                chunkFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
                return new ResponseResult(CommonCode.FAIL);
            }
        }else{
            chunkFile.delete();
        }
        try {
            file.transferTo(chunkFile);
        } catch (IOException e) {
            e.printStackTrace();
            ExceptionCast.cast(MediaCode.CHUNK_FILE_UPLOAD_FAIL);
        }

        return new ResponseResult(CommonCode.SUCCESS);
    }

    public ResponseResult mergeChunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt, Integer chunkNum){
        //
        String chunkFolderPath = getChunkFolder(fileMd5);
        File chunkFolder = new File(chunkFolderPath);
        List<File> chunkFileList = getChunkFileList(chunkFolder);
        chunkFileList = chunkFileList.subList(0, chunkNum);
        System.out.println("分片的数量为" + chunkNum);
        String filePath = getFilePath(fileMd5, fileExt);
        File file = new File(filePath);
        //合并文件
        try {
            mergeFile(chunkFileList, file);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //检查MD5值
        boolean checkFileMd5OK = checkFileMd5(file, fileMd5);
        if(!checkFileMd5OK){
            ExceptionCast.cast(MediaCode.MERGE_CHUNK_FILE_CHECK_FILE_MD5_FAIL);
        }

        //将信息保存到mongodb中
        MediaFile mediaFile = new MediaFile();
        mediaFile.setFileId(fileMd5);
        mediaFile.setFileName(fileMd5 + "." + fileExt);
        mediaFile.setFileOriginalName(fileName);
        mediaFile.setFileSize(fileSize);
        mediaFile.setFilePath(getFileFolderRelativePath(fileMd5));
        mediaFile.setMimeType(mimetype);
        mediaFile.setFileType(fileExt);
        mediaFile.setFileStatus("301002");
        mediaFile.setUploadTime(new Date());
        MediaFile save = mediaFileRepository.save(mediaFile);

        //发送消息
        sendProcessVideoMsg(save.getFileId());
        return new ResponseResult(CommonCode.SUCCESS);
    }

    public ResponseResult sendProcessVideoMsg(String mediaId){
        Optional<MediaFile> mediaFileOptional = mediaFileRepository.findById(mediaId);
        if(!mediaFileOptional.isPresent()){
            return new ResponseResult(CommonCode.FAIL);
        }
        MediaFile mediaFile = mediaFileOptional.get();
        Map<String, Object> data = new HashMap<>();
        data.put("mediaId", mediaId);
        String msg = JSONObject.toJSONString(data);
        try {
            rabbitTemplate.convertAndSend(RabbitMQConfig.EX_MEDIA_PROCESSTASK, RabbitMQConfig.routingkey_media_video , msg);
        } catch (AmqpException e) {
            e.printStackTrace();
            log.info("发送媒资处理消息失败，msg={},e={}", msg, e.getMessage());
            return new ResponseResult(CommonCode.FAIL);
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    private boolean checkFileMd5(File file, String fileMd5){
        if(Objects.isNull(file) || StringUtils.isEmpty(fileMd5)){
            return false;
        }
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            String fileMd5Generate = DigestUtils.md5DigestAsHex(inputStream);
            System.out.println(fileMd5Generate);
            System.out.println(fileMd5);
            if(fileMd5.equals(fileMd5Generate)){
                return true;
            }else{
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(Objects.nonNull(inputStream)){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }


    public static void main(String[] args) throws IOException {
        //File file = new File("D:\\00\\01\\demo_01\\第二讲：数字色彩理论.mp4");
        File file = new File("D:\\00\\01\\demo_01\\f\\a\\fade066cf36155c2aec9091f587657ba\\fade066cf36155c2aec9091f587657ba.mp4");
        String s = DigestUtils.md5DigestAsHex(new FileInputStream(file));
        //130047f68870d0d12970117aed58f180
        //130047f68870d0d12970117aed58f180
        //130047f68870d0d12970117aed58f180
/*
D:\00\01\demo_01> CertUtil -hashfile 第二讲：数字色彩理论.mp4 MD5
MD5 的 第二讲：数字色彩理论.mp4 哈希:
130047f68870d0d12970117aed58f180

D:\00\01\demo_01\f\a\fade066cf36155c2aec9091f587657ba> CertUtil -hashfile fade066cf36155c2aec9091f587657ba.mp4 MD5
MD5 的 fade066cf36155c2aec9091f587657ba.mp4 哈希:
130047f68870d0d12970117aed58f180
*/
        System.out.println(s);
    }

    private boolean checkFileMd52(File mergeFile,String md5){
        if(mergeFile == null || StringUtils.isEmpty(md5)){
            return false;
        }
        //进行md5校验
        FileInputStream mergeFileInputstream = null;
        try {
            mergeFileInputstream = new FileInputStream(mergeFile);
            //得到文件的md5
//            String  mergeFileMd5 = DigestUtils.md5Hex(mergeFileInputstream);
//            //比较md5
//            if(md5.equalsIgnoreCase(mergeFileMd5)){
//               return true;
//            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                mergeFileInputstream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private void mergeFile(List<File> chunkFileList, File destFile) throws IOException {
        FileOutputStream fileOutputStream = new FileOutputStream(destFile);
        for (File file : chunkFileList) {
            FileInputStream inputStream = new FileInputStream(file);
            byte[] buf = new byte[1024];
            int len = -1;
            while((len = inputStream.read(buf)) != -1){
                fileOutputStream.write(buf, 0, len);
            }
            inputStream.close();
        }
        fileOutputStream.close();
    }

    private List<File> getChunkFileList(File chunkFolder) {
        File[] chunkFileArray = chunkFolder.listFiles(file->file.isFile());
        List<File> chunkFileList = new ArrayList<>(Arrays.asList(chunkFileArray));
        chunkFileList.sort((a,b)->Integer.parseInt(a.getName()) - Integer.parseInt(b.getName()));
        return chunkFileList;
    }
}
